//package com.leo.foodmaster.commons.inteceptor;
//
//import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
//import com.leo.foodmaster.admin.system.entity.UserBean;
//import com.leo.foodmaster.commons.util.WebUtils;
//import org.apache.ibatis.cache.CacheKey;
//import org.apache.ibatis.executor.Executor;
//import org.apache.ibatis.mapping.BoundSql;
//import org.apache.ibatis.mapping.MappedStatement;
//import org.apache.ibatis.mapping.ParameterMapping;
//import org.apache.ibatis.plugin.*;
//import org.apache.ibatis.reflection.MetaObject;
//import org.apache.ibatis.session.Configuration;
//import org.apache.ibatis.session.ResultHandler;
//import org.apache.ibatis.session.RowBounds;
//import org.apache.ibatis.type.TypeHandlerRegistry;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.util.Assert;
//import org.springframework.web.context.request.RequestContextHolder;
//import org.springframework.web.context.request.ServletRequestAttributes;
//import javax.annotation.Resource;
//import javax.servlet.http.HttpServletRequest;
//import java.text.DateFormat;
//import java.util.*;
//import java.util.regex.Matcher;
//
///**
// * <p>
// * MyBatis拦截器
// * </p>
// *
// *
// * 参考文献：http://www.yangxuwang.com/jingyan/1533818219451005
// * <p>
// * 定义自己的Interceptor最重要的是要实现plugin方法和intercept方法，在plugin方法中我们可以决定是否要进行拦截进而决定要返回
// * 一个什么样的目标对象。而intercept方法就是要进行拦截的时候要执行的方法。
// * <p>
// * 对于实现自己的Interceptor而言有两个很重要的注解，一个是@Intercepts，其值是一个@Signature数组。@Intercepts用于表明当前的对象是一个Interceptor，
// * 而@Signature则表明要拦截的接口、方法以及对应的参数类型
// * Mybatis支持对Executor、StatementHandler、ParameterHandler和ResultSetHandler进行拦截，也就是说会对这4种对象进行代理
// *
// * method：表示拦截的方法，mybatis支持的方法有 update, query, flushStatements, commit, rollback, getTransaction, close, isClosed
// * 方法，其中，update包括新增、修改、删除等方法，query用于查询，其它的基本用不到。
// * args：表示拦截的参数类型，有MappedStatement、Object、RowBounds和ResultHandler等等.
// * type：表示拦截的类，有Executor、StatementHandler、ParameterHandler和ResultSetHandler。
// *
// * @author ：Leo
// * @since ：2021/10/20 14:20
// */
//@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
//             @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class,
//                        CacheKey.class, BoundSql.class})})
//public class MybatisInterceptor implements Interceptor {
//    @Resource
//    private RedisTemplate<String, String> redisTemplate;
//
//
//
//    /**
//     * intercept方法就是要进行拦截的时候要执行的方法。
//     */
//    @Override
//    public Object intercept(Invocation invocation) throws Throwable {
//        try {
//            // 获取xml中的一个select/update/insert/delete节点，是一条SQL语句
//            MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
//            Object parameter = null;
//            // 获取参数，if语句成立，表示sql语句有参数，参数格式是map形式
//            if (invocation.getArgs().length > 1) {
//                parameter = invocation.getArgs()[1];
//                System.out.println("parameter = " + parameter);
//            }
//            String sqlId = mappedStatement.getId(); // 获取到节点的id,即sql语句的id
//            System.out.println("sqlId = " + sqlId);
//            BoundSql boundSql = mappedStatement.getBoundSql(parameter); // BoundSql就是封装myBatis最终产生的sql类
//            Configuration configuration = mappedStatement.getConfiguration(); // 获取节点的配置
//            String sql = getSql(configuration, boundSql, sqlId); // 获取到最终的sql语句
//            System.out.println("sql = " + sql);
//
//            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//            Assert.notNull(attributes, "ServletRequestAttributes is null !");
//            HttpServletRequest request = attributes.getRequest();
//            String uri = request.getRequestURI();
//            UserBean user = WebUtils.getUser();
//            String account;
//            if (user == null) {
//                account = "anonymous";
//            }else {
//                account = user.getAccount();
//            }
//            List<String> list = redisTemplate.opsForList().range("sql-" + uri + "-" + account, 0, -1);
//            if(list != null) {
//                redisTemplate.opsForList().leftPush("sql-" + uri + "-" + account, sql);
//            }
//
//        }
//        catch (Exception e) {
//            e.printStackTrace();
//        }
//        // 执行完上面的任务后，不改变原有的sql执行过程
//        return invocation.proceed();
//    }
//
//
//    // 封装了一下sql语句，使得结果返回完整xml路径下的sql语句节点id + sql语句
//    public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {
//        String sql = showSql(configuration, boundSql);
//        return sqlId + ":" + sql;
//    }
//
//
//    // 如果参数是String，则添加单引号， 如果是日期，则转换为时间格式器并加单引号； 对参数是null和不是null的情况作了处理
//    private static String getParameterValue(Object obj) {
//        String value;
//        if (obj instanceof String) {
//            value = "'" + obj.toString() + "'";
//        }
//        else if (obj instanceof Date) {
//            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
//                    DateFormat.DEFAULT, Locale.CHINA);
//            value = "'" + formatter.format(new Date()) + "'";
//        }
//        else {
//            if (obj != null) {
//                value = obj.toString();
//            }
//            else {
//                value = "";
//            }
//        }
//        return value;
//    }
//
//
//    /**
//     * 进行？的替换
//     */
//    public static String showSql(Configuration configuration, BoundSql boundSql) {
//        // 获取参数
//        Object parameterObject = boundSql.getParameterObject();
//        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//        // sql语句中多个空格都用一个空格代替
//        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
//        if (CollectionUtils.isNotEmpty(parameterMappings) && parameterObject != null) {
//            // 获取类型处理器注册器，类型处理器的功能是进行java类型和数据库类型的转换
//            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
//            // 如果根据parameterObject.getClass(）可以找到对应的类型，则替换
//            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
//                sql = sql.replaceFirst("\\?",
//                        Matcher.quoteReplacement(getParameterValue(parameterObject)));
//
//            }
//            else {
//                // MetaObject主要是封装了originalObject对象，提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
//                MetaObject metaObject = configuration.newMetaObject(parameterObject);
//                for (ParameterMapping parameterMapping : parameterMappings) {
//                    String propertyName = parameterMapping.getProperty();
//                    if (metaObject.hasGetter(propertyName)) {
//                        Object obj = metaObject.getValue(propertyName);
//                        sql = sql.replaceFirst("\\?",
//                                Matcher.quoteReplacement(getParameterValue(obj)));
//                    }
//                    else if (boundSql.hasAdditionalParameter(propertyName)) {
//                        // 该分支是动态sql
//                        Object obj = boundSql.getAdditionalParameter(propertyName);
//                        sql = sql.replaceFirst("\\?",
//                                Matcher.quoteReplacement(getParameterValue(obj)));
//                    }
//                    else {
//                        // 打印出缺失，提醒该参数缺失并防止错位
//                        sql = sql.replaceFirst("\\?", "缺失");
//                    }
//                }
//            }
//        }
//        return sql;
//    }
//
//    /**
//     * plugin方法是拦截器用于封装目标对象的，通过该方法我们可以返回目标对象本身，也可以返回一个它的代理。
//     * 当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法，当然也可以调用其他方法
//     * 对于plugin方法而言，其实Mybatis已经为我们提供了一个实现。Mybatis中有一个叫做Plugin的类，
//     * 里面有一个静态方法wrap(Object target,Interceptor interceptor)，通过该方法可以决定要返回的对象是目标对象还是对应的代理。
//     */
//    @Override
//    public Object plugin(Object o) {
//        // 只拦截Executor对象,减少目标被代理的次数
//        if (o instanceof Executor) {
//            return Plugin.wrap(o, this);
//        }
//        return o;
//    }
//
//    /**
//     * setProperties方法是用于在Mybatis配置文件中指定一些属性的
//     * 这个方法在Configuration初始化当前的Interceptor时就会执行
//     */
//    @Override
//    public void setProperties(Properties properties) {
//        // do nothing
//    }
//
//}
